// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System.Threading;
using System.Threading.Tasks;
using System.Data;

namespace SnapObjects.Data
{
    internal class ModelUpdateCommand : UpdateCommand
    {
        public ModelUpdateCommand(DataContext dataContext)
            : base(dataContext)
        {
        }

        public void Execute(IUpdatableModel updatableModel)
        {
            this.InitCommand();

            _dataContext.CurrentConnection.Open();
            IAdoDbDataVisitor result = null;

            try
            {
                var modelUpdateInfo = new ModelUpdateInfo(
                                            updatableModel.ModelMeta.MappedTable,
                                            updatableModel.ModelMeta.MappedSchema,
                                            updatableModel,
                                            new ParameterNameGenerator("p").GenerateNext);

                var commandBuilder = new ModelCommandBuilder(_dataContext);

                if (commandBuilder.BuildCommand(modelUpdateInfo))
                {
                    this.BuiltSql =
                        _dataContext.AdoDbSqlProvider.SqlGenerationHelper.SqlBegin
                        + commandBuilder.BuiltSql
                        + _dataContext.AdoDbSqlProvider.SqlGenerationHelper.SqlEnd;

                    result = _adoDbProvider.ExecuteSqlQuery(
                        this.BuiltSql, commandBuilder.GetAdoDbParameters(), 
                        CommandBehavior.Default);
                    
                    this.ConsumeResult(modelUpdateInfo, commandBuilder.FirstOrDefaultResultSetMapping, result);
                }
            }
            finally
            {
                result?.Dispose();
                _dataContext?.CurrentConnection?.Close();
            }
        }

        public async Task ExecuteAsync(IUpdatableModel updatableModel, CancellationToken cancellationToken)
        {
            this.InitCommand();

            await _dataContext.CurrentConnection.OpenAsync(cancellationToken);

            IAdoDbDataVisitor result = null;

            try
            {
                var modelUpdateInfo = new ModelUpdateInfo(
                                            updatableModel.ModelMeta.MappedTable,
                                            updatableModel.ModelMeta.MappedSchema,
                                            updatableModel,
                                            new ParameterNameGenerator("p").GenerateNext);

                var commandBuilder = new ModelCommandBuilder(_dataContext);

                if (commandBuilder.BuildCommand(modelUpdateInfo))
                {
                    this.BuiltSql =
                        _dataContext.AdoDbSqlProvider.SqlGenerationHelper.SqlBegin
                        + commandBuilder.BuiltSql
                        + _dataContext.AdoDbSqlProvider.SqlGenerationHelper.SqlEnd;

                    result = await _adoDbProvider.ExecuteSqlQueryAsync(
                        this.BuiltSql, commandBuilder.GetAdoDbParameters(), cancellationToken);

                    this.ConsumeResult(modelUpdateInfo, commandBuilder.FirstOrDefaultResultSetMapping, result);
                }
            }
            finally
            {
                if (result != null)
                {
                    await result.DisposeAsync();
                }

                await _dataContext?.CurrentConnection?.CloseAsync();
            }
        }
    }
}
